/*
 * Copyright 2003-2007 (c) Live Media Pty Ltd. <argot@einet.com.au> 
 *
 * This software is licensed under the Argot Public License 
 * which may be found in the file LICENSE distributed 
 * with this software.
 *
 * More information about this license can be found at
 * http://www.einet.com.au/License
 * 
 * The Developer of this software is Live Media Pty Ltd,
 * PO Box 4591, Melbourne 3001, Australia.  The license is subject 
 * to the law of Victoria, Australia, and subject to exclusive 
 * jurisdiction of the Victorian courts.
 */

package com.colony.networkvm;

import java.io.IOException;
import java.util.HashMap;

import com.argot.TypeException;
import com.argot.TypeLibrary;
import com.argot.network.DynamicClientTypeMap;
import com.argot.network.TypeClient;
import com.argot.remote.MetaInterface;
import com.argot.remote.MetaLocation;
import com.argot.remote.MetaMethod;
import com.argot.remote.MetaObject;
import com.colony.ColonyEnvironment;
import com.colony.IColonyClient;
import com.colony.IContainer;
import com.colony.IProxyFactory;
import com.colony.channel.ChannelClient;
import com.colony.channel.ChannelLoader;
import com.colony.concurrent.SystemThreadPool;
import com.colony.networkvm.machine.NvmInstance;
import com.colony.networkvm.machine.NvmState;
import com.colony.networkvm.proxy.NvmMetaStubFactory;
import com.colony.remote.RemoteException;
import com.colony.remote.rpc.RpcMetaClient;
import com.colony.remote.rpc.RpcMetaMethodRequest;
import com.colony.remote.rpc.RpcMetaStubFactory;
import com.colony.transport.Client;

public class NvmClient 
implements IColonyClient
{
	private boolean _isBound;
	private HashMap _factoryMap;
	private Client _transport;
	private Client _channel;
	private NvmDelegateFactory _delegateFactory;
	
	private RpcMetaClient _rpcMetaClient;
	private MetaLocation _baseLocation;
	private MetaMethod _processMethod;
	
	// Bound variables.
	private String _hostName;
	private ColonyEnvironment _environment;
	private TypeLibrary _library;
	private NvmEnvironment _nvmEnvironment;
	
	private INvmHost _host;

	public NvmClient( Client transport, Client channel, NvmDelegateFactory delegateFactory )
	{
		_factoryMap = new HashMap();
		_isBound = false;
		_transport = transport;
		_channel = channel;
		_delegateFactory = delegateFactory;
		
		_environment = null;
		_library = null;
	}

	public IContainer getHostContainer()
	{
		return _environment.getHostContainer();
	}

	public TypeLibrary getTypeLibrary()
	{
		return _library;
	}

	public NvmDelegateFactory getDelegateFactory()
	{
		return _delegateFactory;
	}
	
	public Object getFront( MetaLocation location ) 
	throws RemoteException, TypeException
	{
		MetaObject metaObject = _host.getObject(location);
		Class clss = _library.getClass(metaObject.getType());
		return getFactory(clss).getProxy(location);
	}

	public Object getFront( MetaObject metaObject ) 
	throws RemoteException, TypeException
	{
		Class clss = _library.getClass(metaObject.getType());
		return getFactory(clss).getProxy(metaObject.getLocation());
	}
	
	public IProxyFactory getFactory( Class clss )
	throws TypeException
	{
		IProxyFactory factory = (IProxyFactory) _factoryMap.get(clss);
		if (factory == null )
		{
			MetaInterface metaInterface = (MetaInterface) _library.getStructure(_library.getId(clss));
			factory = new NvmMetaStubFactory( _hostName, _nvmEnvironment, metaInterface, clss );
			setFactory( clss, factory );
		}
		return factory;
	}

	public void setFactory( Class clss, IProxyFactory factory )
	{
		_factoryMap.put(clss,factory);
	}

	public NvmInstance getNvmInstance()
	{
		return new NvmInstance(_nvmEnvironment, _library);
	}

	public void executeInstance( NvmInstance instance )
	throws RemoteException
	{
		instance.execute(instance);
	}

	public void sendToHost( NvmInstance instance )
	throws RemoteException
	{
		Object[] args = new Object[1];
		args[0] = instance.getState();
		RpcMetaMethodRequest methodRequest = new RpcMetaMethodRequest(_baseLocation,_processMethod,args);
		try {
			_rpcMetaClient.process(methodRequest);
		} catch (IOException e) {
			throw new RemoteException("NvmClient: failed to send NvmState to host", e);
		}
		instance.setState( (NvmState) methodRequest.getReturnValue() );
	}

	public void bind( String hostName, ColonyEnvironment environment ) 
	throws RemoteException, TypeException, IOException
	{
		if ( _isBound ) throw new RemoteException( "Client already bound" );
		_isBound = true;
		
		_hostName = hostName;
		_environment = environment;
		_library = _environment.getTypeLibrary();
		_nvmEnvironment = new NvmEnvironment( _environment, _delegateFactory, _library );
		
		TypeClient typeClient = new TypeClient( _library, _transport );
		DynamicClientTypeMap dynMap = new DynamicClientTypeMap( _library, typeClient );
		_rpcMetaClient = new RpcMetaClient( typeClient, dynMap );
		
		_processMethod = (MetaMethod) _library.getStructure(_library.getId("nvm.host.process"));
		
		// get the base object.
		MetaObject baseObject = typeClient.getBaseObject( dynMap );
		_baseLocation = baseObject.getLocation();
		MetaInterface metaClass = (MetaInterface) _library.getStructure( baseObject.getType() );
		Class clss = _library.getClass( baseObject.getType() );
		RpcMetaStubFactory factory = new RpcMetaStubFactory(_rpcMetaClient, metaClass, clss);
		Object o = factory.getProxy( baseObject.getLocation() );

		// setup the channel client.

		if ( _channel != null )
		{
			ChannelClient channelClient = new ChannelClient( _channel, _library, SystemThreadPool.getThreadPool() );
			ChannelLoader.bindClientMap( dynMap, channelClient );
		}

		// add the host to our local environment.
		_host = (INvmHost) o;
		
	}

	public boolean isBound()
	{
		return _isBound;
	}
	
}
